package com.sos.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.time.LocalDateTime;

@Service
@RequiredArgsConstructor
@Slf4j
public class UserLocationService {

    private final RedisTemplate<String, Object> redisTemplate;
    
    // 位置信息过期时间（秒）- 30分钟
    private static final long LOCATION_EXPIRE_SECONDS = 30 * 60;
    
    // Redis键前缀
    private static final String USER_LOCATION_PREFIX = "sos:location:user:";
    
    // 位置变化阈值（单位：度），约等于11米
    private static final double LOCATION_THRESHOLD = 0.0001;
    
    // 记录上次更新时间，避免过于频繁的更新
    private final Map<Long, LocalDateTime> lastUpdateTimes = new ConcurrentHashMap<>();
    
    // 最小更新间隔（秒）
    private static final long MIN_UPDATE_INTERVAL = 5;

    /**
     * 更新用户位置信息
     * @param userId 用户ID
     * @param latitude 纬度
     * @param longitude 经度
     */
    public void updateUserLocation(Long userId, BigDecimal latitude, BigDecimal longitude) {
        try {
            log.info("开始更新用户 {} 的位置信息: ({}, {})", userId, latitude, longitude);
            // 检查更新频率
            LocalDateTime now = LocalDateTime.now();
            LocalDateTime lastUpdate = lastUpdateTimes.get(userId);
            
            if (lastUpdate != null) {
                long secondsSinceLastUpdate = java.time.Duration.between(lastUpdate, now).getSeconds();
                if (secondsSinceLastUpdate < MIN_UPDATE_INTERVAL) {
                    log.debug("用户 {} 位置更新过于频繁，跳过更新 (距离上次更新 {} 秒)", userId, secondsSinceLastUpdate);
                    return;
                }
            }
            
            // 检查位置变化
            if (latitude != null && longitude != null && !isLocationChangedSignificantly(userId, latitude, longitude)) {
                log.debug("用户 {} 位置变化较小，跳过更新", userId);
                // 即使位置变化小，也要更新最后更新时间，防止位置长时间不更新
                lastUpdateTimes.put(userId, now);
                return;
            }
            
            String key = USER_LOCATION_PREFIX + userId;
            
            // 使用Hash结构存储位置信息
            redisTemplate.opsForHash().putAll(key, new java.util.HashMap<String, Object>() {{
                put("latitude", latitude != null ? latitude.toString() : null);
                put("longitude", longitude != null ? longitude.toString() : null);
                put("timestamp", String.valueOf(System.currentTimeMillis()));
            }});
            
            // 设置过期时间
            redisTemplate.expire(key, LOCATION_EXPIRE_SECONDS, TimeUnit.SECONDS);
            
            // 记录更新时间
            lastUpdateTimes.put(userId, now);
            
            log.info("用户 {} 位置已更新: ({}, {})", userId, latitude, longitude);
        } catch (Exception e) {
            log.error("更新用户 {} 位置信息失败", userId, e);
        }
    }

    /**
     * 获取用户位置信息
     * @param userId 用户ID
     * @return 位置信息Map，包含latitude、longitude和timestamp
     */
    public Map<Object, Object> getUserLocation(Long userId) {
        try {
            String key = USER_LOCATION_PREFIX + userId;
            return redisTemplate.opsForHash().entries(key);
        } catch (Exception e) {
            log.error("获取用户 {} 位置信息失败", userId, e);
            return null;
        }
    }

    /**
     * 批量获取多个用户位置信息
     * @param userIds 用户ID列表
     * @return 用户位置信息映射
     */
    public java.util.Map<Long, Map<Object, Object>> getBatchUserLocations(java.util.List<Long> userIds) {
        java.util.Map<Long, Map<Object, Object>> result = new java.util.HashMap<>();
        try {
            for (Long userId : userIds) {
                Map<Object, Object> location = getUserLocation(userId);
                if (location != null && !location.isEmpty()) {
                    result.put(userId, location);
                }
            }
        } catch (Exception e) {
            log.error("批量获取用户位置信息失败", e);
        }
        return result;
    }

    /**
     * 删除用户位置信息
     * @param userId 用户ID
     */
    public void removeUserLocation(Long userId) {
        try {
            String key = USER_LOCATION_PREFIX + userId;
            redisTemplate.delete(key);
            lastUpdateTimes.remove(userId);
            log.debug("用户 {} 位置信息已删除", userId);
        } catch (Exception e) {
            log.error("删除用户 {} 位置信息失败", userId, e);
        }
    }
    
    /**
     * 检查位置变化是否显著
     * @param userId 用户ID
     * @param newLatitude 新纬度
     * @param newLongitude 新经度
     * @return 是否变化显著
     */
    private boolean isLocationChangedSignificantly(Long userId, BigDecimal newLatitude, BigDecimal newLongitude) {
        Map<Object, Object> oldLocation = getUserLocation(userId);
        if (oldLocation == null || oldLocation.isEmpty()) {
            log.debug("用户 {} 没有旧位置信息，认为变化显著", userId);
            return true; // 没有旧位置信息，认为变化显著
        }
        
        try {
            Object oldLatObj = oldLocation.get("latitude");
            Object oldLonObj = oldLocation.get("longitude");
            
            if (oldLatObj == null || oldLonObj == null) {
                log.debug("用户 {} 旧位置信息不完整，认为变化显著", userId);
                return true; // 旧位置信息不完整
            }
            
            BigDecimal oldLatitude = new BigDecimal(oldLatObj.toString());
            BigDecimal oldLongitude = new BigDecimal(oldLonObj.toString());
            
            // 计算位置变化距离
            double distance = calculateDistance(oldLatitude, oldLongitude, newLatitude, newLongitude);
            
            boolean isSignificant = distance > LOCATION_THRESHOLD;
            log.debug("用户 {} 位置变化距离: {}，阈值: {}，变化{}显著", 
                    userId, distance, LOCATION_THRESHOLD, isSignificant ? "是" : "不");
            
            // 判断变化是否超过阈值
            return isSignificant;
        } catch (Exception e) {
            log.warn("计算用户 {} 位置变化时出错，假设变化显著: {}", userId, e.getMessage());
            return true;
        }
    }
    
    /**
     * 计算两点之间的距离（简化计算，单位：度）
     */
    private double calculateDistance(BigDecimal lat1, BigDecimal lon1, BigDecimal lat2, BigDecimal lon2) {
        // 使用简单的欧几里得距离计算（在小范围内足够准确）
        double dLat = lat2.subtract(lat1).doubleValue();
        double dLon = lon2.subtract(lon1).doubleValue();
        return Math.sqrt(dLat * dLat + dLon * dLon);
    }
}